CSRF란?

신뢰할 수 있는 사용자를 사칭해 웹 사이트에 원하지 않는 명령어를 보내는 공격 기

CSRF 해킹 조건

  1. 보안이 취약한 서버로부터 이미 로그인되어 있는 상태여야 함.
  2. 쿠키 기반의 서버 세션 정보를 획득할 수 있어야 함.
  3. 공격자는 서버를 공격하기 위한 요청 방법에 대해 미리 파악하고 있어야함.

CSRF 해킹 동작 원리

  1. 보안이 취약한 서버에 로그인
  2. 서버에 저장된 세션 정보를 사용할 수 있는 session ID가 사용자의 브라우저 쿠키에 저장됨
  3. 사용자가 악성 스크립트 페이지를 누르도록 유도
  4. 악성 스크립트를 직접 전달 혹은 웹 브라우저에 의해 쿠키에 저장된 session ID와 함께 서버로 요청.
  5. 서버에 담긴 session ID를 통해 해당 요청이 인증된 사용자로부터 온 것으로 판단, 처리

CSRF 예방 방법

  1. 의심 되는 URL 조심하기 [사용자 입장]

    • 의심되는 URL을 함부로 클릭하지 않고, 의심되는 메일을 열어보지 않는 것.
  2. Referer check [개발자, 운영자 입장]

    • HTTP 요청 헤더 정보에 있는 Referrer정보와 호스트 정보를 비교해서 일치해야 함.
    • JAVA servlet 경우 intercepter클래스를 만들어서 모든 요청에 대해 방버 가능.
    public class ReferrerCheck implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String referer = request.getHeader("Referer");
            String host = request.getHeader("host");
            if (referer == null || !referer.contains(host)) {
                response.sendRedirect("/");
                return false;
            }
            return true;
        }
    }
  3. CAPTCHA [개발자, 운영자 입장]

    • CAPTCHA를 이용하여, 인증코드가 없거나, 요청을 거부하도록 가능.
  4. CSRF 토큰 사용 [개발자, 운영자 입장]

    • 세션에 임의에 값을 저장 → 모든 요청에 해당 값을 포함하여 전송.
    • 세션에 저장된 값과 요청으로 전송된 값이 일치한지를 바타응로 검증.
session.setAttribute("CSRF_TOKEN", UUID.randomUUID().toString());
// session value에 CSRF_TOKEN값 저장
<form action="<http://example/path>" method="POST">
   <input type="hidden" name="CSRF_TOKEN" value="${CSRF_TOKEN}">
 <!-- ... -->
</form>
//CSRF_TOKEN 값을 전송하도록 해
public class CsrfTokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession httpSession = request.getSession();
        String csrfTokenParam = request.getParameter("CSRF_TOKEN");
        String csrfTokenSession = (String) httpSession.getAttribute("CSRF_TOKEN");
        if (csrfTokenParam == null || !csrfTokenParam.equals(csrfTokenSession)) {
            response.sendRedirect("/");
            return false;
        }
        return true;
    }
}
//요청 받을시, 인터셉터에서 CSRF_TOKEN값 검증하도록 함.